﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using System.Text.RegularExpressions;
using System.Xml.Serialization;

namespace UDS.Build.Database
{
    [LoadInSeparateAppDomain]
    public class BuildGrantFile : MarshalByRefObject, ITask
    {
        static Regex sprocRegex = new Regex(@"create\s*proc\S*\s+(\[?dbo\]?\.)?\[?([^\s\]]+)\]?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
        static Regex functionRegex = new Regex(@"create\s*func\S*\s+(\[?dbo\]?\.)?\[?([^\s\]]+)\]?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
        static Regex createAtLeastRegex = new Regex(@"create\s*", RegexOptions.Compiled | RegexOptions.IgnoreCase);

        protected TaskLoggingHelper Log;

        [Required]
        public string ProjectDir
        {
            get;
            set;
        }

        public BuildGrantFile()
        {
            Log = new TaskLoggingHelper(this);
        }


        [Required]
        public ITaskItem[] EmbeddedResources
        {
            get;
            set;
        }

        [Required]
        public ITaskItem[] StoredProcedures
        {
            get;
            set;
        }

        [Required]
        public string ProjectName
        {
            get;
            set;
        }

        #region ITask Members

        IBuildEngine ITask.BuildEngine
        {
            get;
            set;
        }

        ITaskHost ITask.HostObject
        {
            get;
            set;
        }

#if false
        private List<string> serverSecurityRoles = null;

        private List<string> ServerSecurityRoles
        {
            get
            {
                if (serverSecurityRoles == null)
                {
                    serverSecurityRoles = new List<string>();
                    string[] defaultRoles = Microsoft.Iptv.Server.Management.Data.Deployment.ServerSecurityHelper.GetDatabaseRoles(ProjectToFeatureMap.GetFeatureNameForProject(ProjectName));
                    foreach (string role in defaultRoles)
                    {
                        serverSecurityRoles.Add(role.ToLower()); 
                    }
                }
                return serverSecurityRoles;
            }
        }
#endif

        public bool Execute()
        {
            try
            {
                /*System.Diagnostics.Debugger.Break();
                foreach (ITaskItem item in StoredProcedures)
                {
                    IEnumerator etor = item.MetadataNames.GetEnumerator();
                    while (etor.MoveNext())
                    {
                        Console.WriteLine(string.Format("{0} : {1}", etor.Current.ToString(), item.GetMetadata(etor.Current.ToString())));
                    }
                }*/
                Dictionary<string, SQLObject> dList = new Dictionary<string, SQLObject>();

                Grants grants = new Grants();
                foreach (ITaskItem item in StoredProcedures)
                {
                    string src = item.GetMetadata("Link");
                    string identity = item.GetMetadata("Identity");
                    if (string.IsNullOrEmpty(src))
                    {
                        src = identity;
                    }
                    SQLObject sec = ParseForStoredProcedureName(ProjectDir + identity);
                    if (sec == null)
                    {
                        throw new Exception(string.Format("Failed to get the name of the stored procedure from file, please verify if the stored procedure file [{0}] is a valid SQL file", ProjectDir + identity));
                    }
                    if (sec is Function)
                    {
                        throw new Exception(string.Format("Functions are not supposed to have permissions to any IPTV roles. please reset the file [{0}] properties in the .csproj to be EmbeddedResource.", ProjectDir + identity));
                    }

                    if (!string.IsNullOrEmpty(sec.Name))
                    {
                        sec.Source = src;
#if false
                        string tempList = item.GetMetadata("SQLRoles");

                        if (string.IsNullOrEmpty(tempList))
                        {
                            IEnumerator etor = item.MetadataNames.GetEnumerator();
                            bool haveSQLRolesSpecified = false;
                            while (etor.MoveNext())
                            {
                                if (string.Compare("SQLRoles", etor.Current.ToString(), true) == 0)
                                {
                                    haveSQLRolesSpecified = true;
                                    break;
                                }
                            }
                            if (haveSQLRolesSpecified)
                            {
                                sec.SQLRole = new string[0];
                            }
                            else
                            {
                                sec.SQLRole = null;
                            }
                        }
                        else
                        {
                            sec.SQLRole = BuildRolesFromCSProject(tempList, sec.Name);
                        }
#endif

                        dList.Add(sec.Source.ToLower(), sec);
                    }
                }
                grants.SQLObjects = new GrantsSQLObjects();
                grants.SQLObjects.StoredProcedures = new StoredProcedure[dList.Values.Count];
                dList.Values.CopyTo(grants.SQLObjects.StoredProcedures, 0);

                foreach (ITaskItem fitem in EmbeddedResources)
                {
                    string src = fitem.GetMetadata("Link");
                    if (string.IsNullOrEmpty(src))
                    {
                        src = fitem.GetMetadata("Identity");
                    }
                    src = src.ToLower();
                    if (src.Contains("storedprocedures"))
                    {
                        if (!(dList.ContainsKey(src)))
                        {
                            SQLObject fsec = ParseForStoredProcedureName(ProjectDir + src);
                            if (fsec != null && (!(string.IsNullOrEmpty(fsec.Name))))
                            {
                                throw new Exception(string.Format("Stored procedure [{0}] must be flagged as StoreProcedure in the file property window instead of EmbeddedResource", src));
                            }
                        }
                    }
                }



                StringWriter stringWriter = new StringWriter();
                XmlSerializer Serializer = new XmlSerializer(typeof(Grants));
                Serializer.Serialize(stringWriter, grants);
                string generatedContent = stringWriter.ToString();

                string grantFileName = ProjectDir + ProjectName + ".sproc";
                string fileStr = string.Empty;
                if (!File.Exists(grantFileName))
                {
                    File.WriteAllText(grantFileName, generatedContent, Encoding.Unicode);
                    PrintStringToConsole(grantFileName + " generated Successfully.", ConsoleColor.DarkGreen);
                }
                else
                {
                    using (StreamReader sr = File.OpenText(grantFileName))
                    {
                        fileStr = sr.ReadToEnd();
                    }
                    if ((string.Compare(fileStr, generatedContent, true) != 0))
                    {
                        File.WriteAllText(grantFileName, generatedContent, Encoding.Unicode);
                        PrintStringToConsole(grantFileName + " generated Successfully.", ConsoleColor.DarkGreen);
                    }
                    else
                    {
                        PrintStringToConsole(grantFileName + " grant file did not change.", ConsoleColor.DarkGray);
                    }
                }
            }
            catch (Exception ex)
            {
                PrintStringToConsole(ex.Message, ConsoleColor.Red);
                throw;
            }
            return true;
        }
        #endregion

        #region Utility functions

#if false
        private string[] BuildRolesFromCSProject(string tempList, string secName)
        {
            if (!string.IsNullOrEmpty(tempList))
            {
                List<string> sgRoleList = new List<string>();
                string[] aList = tempList.Split(new char[]{';', ','});
                foreach (string role in aList)
                {
                    if (!string.IsNullOrEmpty(role))
                    {
                        if (ServerSecurityRoles.Contains(role.ToLower()))
                        {
                            sgRoleList.Add(role);
                        }
                        else
                        {
                            throw new Exception(string.Format("Invalid role name [{0}] specified in the project [{1}] for stored procedure [{2}], it is not defined as part of server security roles list for this feature", role, ProjectName, secName));
                        }
                    }
                }
                return sgRoleList.ToArray();
            }
            else
            {
                return null;
            }
        }
#endif

        static void PrintStringToConsole(string str, ConsoleColor color)
        {
            ConsoleColor consoleColor = Console.ForegroundColor;
            Console.ForegroundColor = color;
            Console.WriteLine(str);
            Console.ForegroundColor = consoleColor;
        }

        static private SQLObject ParseForStoredProcedureName(string filename)
        {
            string fileStr = string.Empty;
            if (!File.Exists(filename))
            {
                PrintStringToConsole(string.Format("file {0} does not exist.", filename), ConsoleColor.Red);
                throw new FileNotFoundException("File is not found", filename);
            }
            using (StreamReader sr = File.OpenText(filename))
            {
                fileStr = sr.ReadToEnd();
            }

            Match match = sprocRegex.Match(fileStr);
            if (match.Success && match.Groups != null && match.Groups.Count > 2)
            {
                StoredProcedure grantsSecurable = new StoredProcedure();
                grantsSecurable.Name = match.Groups[2].Value;
                return grantsSecurable;
            }
            else
            {
                Match funcMatch = functionRegex.Match(fileStr);
                if (funcMatch.Success && funcMatch.Groups != null && funcMatch.Groups.Count > 2)
                {
                    Function grantsSecurable = new Function();
                    grantsSecurable.Name = funcMatch.Groups[2].Value;
                    return grantsSecurable;
                }
                else
                {
                    Match createAtLeastMatch = createAtLeastRegex.Match(fileStr);
                    if (!(createAtLeastMatch.Success))
                    {
                        return new StoredProcedure();
                    }
                    else
                    {
                        return null;
                    }
                }
            }
        }

        #endregion
    }
}
